package com.appspot.moviesessions.infrastructure.scraper;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import com.appspot.moviesessions.model.MovieSessionFoundEvent;

@Component("sessionScraper")
public class GoogleScraper implements Scraper {

	private HttpClientFactory clientFactory;
	private ApplicationEventPublisher eventPublisher;
	private static final String BASE_URL = "http://www.google.com.au/movies";
	private static Log LOG = LogFactory.getLog(GoogleScraper.class);

	@Autowired
	public GoogleScraper(HttpClientFactory clientFactory,
	        ApplicationEventPublisher eventPublisher) {
		this.clientFactory = clientFactory;
		this.eventPublisher = eventPublisher;

	}

	@Override
	public void scrape(Map<String,String> parameters) {
		WebDriver webDriver = clientFactory.create();
		webDriver.get(BASE_URL + "?near=sydney,+nsw,+aus&start=0");

		Date date = Calendar.getInstance().getTime();

		List<WebElement> cinemas = webDriver.findElements(By
		        .cssSelector(".theater"));
		for (WebElement cinema : cinemas) {
			String cinemaName = cinema.findElement(
			        By.cssSelector("div.desc > h2.name > a")).getText();
			String cinemaInfo = cinema.findElement(
			        By.cssSelector("div.desc > div.info")).getText();
			log("cinemaName", cinemaName);
			log("cinemaInfo", cinemaInfo);

			List<WebElement> movies = cinema.findElements(By
			        .cssSelector("div.movie"));
			for (WebElement movie : movies) {
				String movieName = movie.findElement(
				        By.cssSelector("div.name > a")).getText();
				String movieInfo = movie.findElement(
				        By.cssSelector("span.info")).getText();
				log("movieName", movieName);
				log("movieInfo", movieInfo);
				List<WebElement> times = movie.findElements(By
				        .cssSelector("div.times > span"));
				AmPmResolver resolver = new AmPmResolver(date);
				for (WebElement time : times) {
					String text = time.getText();
					resolver.add(text);
				}
				for (Date sessionTime : resolver) {
					eventPublisher.publishEvent(new MovieSessionFoundEvent(
					        this, movieName, sessionTime,cinemaName));
				
				}

			}
		}
	}

	public static class AmPmResolver implements Iterable<Date> {

		private static final DateFormat DATE_FORMAT = new SimpleDateFormat(
		        "yyyy/MM/dd");
		private static final DateFormat INPUT_DATE_TIME_FORMAT = new SimpleDateFormat(
		        "yyyy/MM/dd hh:mmaa");

		private String date;
		private List<String> times = new ArrayList<String>();

		public AmPmResolver(Date date) {
			this.date = DATE_FORMAT.format(date);
		}

		public void add(String time) {
			times.add(time.trim().replaceAll("[^\\p{ASCII}]", ""));
		}

		@Override
		public Iterator<Date> iterator() {
			List<String> iTimes = new ArrayList<String>(times);
			List<Date> sessionTimes = new ArrayList<Date>();
			Collections.reverse(iTimes);
			String currentAmPmSetting = "";
			for (String time : iTimes) {
				if (time.matches(".*(am|pm)")) {
					currentAmPmSetting = time.substring(time.length() - 2);
					sessionTimes.add(parse(date + " " + time));
					continue;
				}
				sessionTimes.add(parse(date + " " + time + currentAmPmSetting));
			}
			Collections.reverse(sessionTimes);
			return sessionTimes.iterator();

		}

		private static Date parse(String date) {
			try {
				return INPUT_DATE_TIME_FORMAT.parse(date);
			} catch (ParseException e) {
				throw new RuntimeException(e);
			}
		}

	}

	private void log(String field, String value) {
		LOG.info(String.format("%s : %s", field, value));
	}

}
